﻿using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;
using Mva.Recognition;
using MvaLib.Diagnostics;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mva.Recognition
{
    public class LocalKeywordRecognizer
        : IDialogBackend
    {
        public event Action<string> KeywordRecognizing;
        public event Action<string> KeywordRecognized;
        public event Action<string> SpeechRecognizing;
        public event Action<string> SpeechRecognized;
        public event Action<DialogErrorInformation> ErrorReceived;


        private KeywordRecognitionModel model = null;
        private KeywordRecognizer recognizer = null;
        private PushAudioInputStream inputStream;
        private IDialogAudioInputProvider audioSource;

        public LocalKeywordRecognizer()
        {
            Debug.WriteLine("Is using LocalKeywordRecognizer.");
        }

        private async Task InitializeKeywordModel()
        {
            var keywordFile = await KeywordRegistration.Keyword.GetConfirmationKeywordFileAsync();
            this.model = KeywordRecognitionModel.FromFile(keywordFile.Path);
        }
        public async Task InitializeAsync()
        {
            if(recognizer == null)
            {
                this.inputStream = AudioInputStream.CreatePushStream();
                recognizer = new KeywordRecognizer(AudioConfig.FromStreamInput(this.inputStream));

                recognizer.Recognized += (s, args) =>
                {
                    Debug.WriteLine("LocalKeywordRecognizer.Recognized");
                    KeywordRecognized?.Invoke("xiaoweixiaowei");
                };

                recognizer.Canceled += (s, args) =>
                {
                    Debug.WriteLine("LocalKeywordRecognizer.Canceled");
                    var code = (int)args.ErrorCode;
                    var message = $"{args.Reason.ToString()}: {args.ErrorDetails}";
                    this.ErrorReceived?.Invoke(new DialogErrorInformation(code, message));
                };
            }

            if (model == null)
            {
                await InitializeKeywordModel().ConfigureAwait(false);
            }
        }

        public void SetAudioSource(IDialogAudioInputProvider source)
        {
            Contract.Requires(source != null);

            this.audioSource = source;
            this.audioSource.DataAvailable += (bytes) =>
            {
                this.inputStream?.Write(bytes.ToArray());
            };
        }

#pragma warning disable CS1998 // 异步方法缺少 "await" 运算符，将以同步方式运行
        public async Task StartAudioTurnAsync(bool performConfirmation)
#pragma warning restore CS1998 // 异步方法缺少 "await" 运算符，将以同步方式运行
        {
            if (recognizer != null)
            {
                _ = recognizer.RecognizeOnceAsync(this.model);
            }
        }

        public async void StopAudioTurn()
        {
            if(recognizer != null)
            {
                await recognizer.StopRecognitionAsync().ConfigureAwait(false);
            }
        }

        #region IDisposable Support
        private bool alreadyDisposed = false;// 要检测冗余调用
        protected virtual void Dispose(bool disposing)
        {
            if (!alreadyDisposed)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    this.recognizer.Dispose();
                    this.inputStream.Dispose();
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                alreadyDisposed = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~DirectLineKeywordBackend()
        // {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion

    }
}
